Conversation
私聊 group 模式下,用「合并转发消息集合 / 图片 / 文件」开的会话群会停在占位名 「新会话」,群里只留一条「(非文本消息)」,看不出这个群是怎么来的。 根因:出生流程的标题来源是 extractMessageTextForRouting 这个文本窥视,它只认 text/post,非文本种子拿到空串。而 scheduleSessionGroupTitle 的空串拦截在**异步体 内**——同步闸已经把这次尝试登记掉了,于是空调用白烧一轮有限重试(共 3 轮)并挂上 30s 退避,群就再也改不了名。 改动: - 出生侧只在窥视真拿到文本时才调度 AI 命名,并用 RoutingContext.sessionGroupTitleScheduled 告知下游;非文本种子改由递归回来的 handleNewTopic 在**消息完整解析之后**调度 (合并转发已展开成 <forwarded_messages>、语音已转写),标题直接从转发内容里总结。 两处严格二选一,避免重复调用再烧一轮重试。 - 新增 forwardMessage(im.v1.message.forward,注意 uuid 在 params 而非 data), 出生时把原私聊消息转发进新群当第一条消息:图片/文件/合并转发没法从事件里复刻, 只有转发能原样带过去。转发件由 bot 自己发出,回声是自发消息,dispatcher 只放行 /close,不会二次触发会话。 - 引言相应简化为一行「原消息已转发到本群」;转发被关掉或失败时回落到旧的正文摘录形态。 - 回复锚点回落链 intro → forwarded,保证始终指向群内消息(会话 rootMessageId / 首轮引用都取这个 id),两者都失败才退化到私聊。 - 新增 sessionGroup.forwardOrigin 开关(默认开)。 Co-Authored-By: Claude Code <noreply@anthropic.com>
|
感谢这个修复 —— 根因找得很准:出生侧 自动评审跑下来有 1 个建议修改项,其余都是可选项。 建议修改:占位符会被当成标题源,且会把自愈路径永久锁死新增的闸(
占位符非空 ⟹ 命名服务不会 return ⟹ 真的起 CLI 跑完 ⟹ 实测(驱动真实的建群递归,只替身外部飞书副作用): 真机 值得留意的是与修复前相比的方向:master 上这条路径喂的是空串 ⟹ 命名服务在 async 体内 return ⟹ 永远不会置 另外 建议改法(已实测可行)在那个闸上加一条「只剩零信息占位符就不调度」,把命名留给后续真实文本自愈: const titleSeed = parsed.content.trim();
// 只剥「零信息」占位符(渲染处见 message-parser 的 imgLabel / 文件分支 /
// AUDIO_PLACEHOLDER / merge_forward 兜底)。带真实文件名或图片 alt 的
// `[文件 1: x.pdf]` / `[图片 2: 图说]` 刻意保留——它们是有效的标题来源。
const seedIsOnlyPlaceholders =
!titleSeed.replace(/\[(?:图片|语音|文件|合并转发消息)(?:\s+\d+)?\]/g, '').trim();
if (sgEntry && !sgEntry.titled && titleSeed && !titleSeed.startsWith('/') && !seedIsOnlyPlaceholders) {
scheduleSessionGroupTitle({ larkAppId, chatId, userText: parsed.content });
}在真实建群递归下实测五种种子:
PR 自有的 4 个套件 26/26 仍全绿, 带真实文件名的 两个配套建议
可选项(不影响合入)
其它已核验
以上是自动评审的初步意见,可能有理解偏差,最终以维护者审阅为准。 |
问题
私聊
p2pMode='group'下,用合并转发的飞书消息集合(以及图片 / 文件等任何非文本消息)开的会话群有两个毛病:根因
出生流程(
maybeBirthSessionGroup)的标题来源是extractMessageTextForRouting,那是个只认text/post的文本窥视,图片 / 文件 / 合并转发在那里一律返回null→ 占位名回落「新会话」、引言回落「(非文本消息)」、scheduleSessionGroupTitle({ userText: '' })。关键在于空串调度不是无害的 no-op:
scheduleSessionGroupTitle的同步闸(in-flight / 重试预算)先把这次尝试登记掉,if (!userText.trim()) return;才在异步体内生效,finally里还会markSessionGroupTitleFailed。于是这一次空调用白烧掉 3 轮有限重试中的一轮并挂上 30s 退避——这正是「转发消息集合开的群从不改名」的直接原因。改了什么
① AI 命名挪到「消息完整解析之后」(非文本种子)
RoutingContext.sessionGroupTitleScheduled告诉下游「已经调过了」;handleNewTopicAdmitted调度——那时parsed.content已经是完整内容(合并转发已展开成<forwarded_messages>、语音已转写),标题直接从转发进来的对话本身总结,比从空串猜强得多;titled/ in-flight 闸在异步体内,出生侧刚发起的那次此刻两者都还没置上,重复调用只会再白烧一轮。② 把原私聊消息转发进新群当第一条消息
forwardMessage()(im.v1.message.forward)。uuid在params里而不是data,与 create / reply 不同,已在 transport boundary 用例里按精确形状断言住;/close,不会二次触发会话;intro → forwarded,保证始终指向群内消息(会话rootMessageId/ 首轮引用都取这个 id),两者都失败才退化到私聊;sessionGroup.forwardOrigin开关,默认开,置false即回到只发引言的旧形态。影响面
src/im/lark/client.ts只新增forwardMessage,未改任何既有原语;RoutingContext只新增一个可选字段;SessionGroupConfig新增一个可选 key(与既有dmReceipt同级,同样无需 dashboard / 文档联动)。p2pMode='group'的会话群出生这一条路径。普通话题会话、solo 群会话、adopt/restore 完全不经过这里;p2pMode非 group 的私聊不受影响。ctx.messageId仍指向原私聊消息(资源下载 / 合并转发子消息的 key 依赖它)等既有不变量均未改动。测试验证
新增
test/session-group-birth-forward-seed.test.ts(8 例,跑真实的建群递归,只替身飞书外部副作用):覆盖:合并转发种子会被转发 + 引言不再是「(非文本消息)」;AI 命名恰好调度一次且
userText是展开后的<forwarded_messages>;占位名仍是「新会话」且titled未置;转发失败 → 引言回落且锚点走 intro;引言失败 → 锚点走转发件(不是私聊消息 id);文本种子对照组仍在出生侧调度一次;forwardOrigin: false时不转发但命名照常。相关既有套件 + 边界:
全量:
这 12 个失败与本改动无关,已逐一证实:
daemon-pinned-working-dir、group-join-shared-routing、doc-comment-audit-gate、doc-comment-daemon-concurrency)单独重跑全绿(4 passed / 69 tests passed),是全量并发下的资源争用抖动;e636f93cb,未带本 PR 任何改动)上跑,失败文件与失败数逐条一致:原因两类:① 本机 bun 是 1.4.0、仓库钉 1.4.2(用例里直接断言
expected '1.4.0' to be '1.4.2'),影响两个 sqlite 套件;② 其余依赖 user namespace / bwrap 等沙箱能力。Live 验证未做(会切走全局 shim 影响所有 bot),本改动的外部副作用只有一次
im.v1.message.forward调用,已在 transport boundary 用例里按精确请求形状断言。🤖 Generated with Claude Code